#include "Spark/Net/MsgBuffer.h"
#include <assert.h>
#include <string.h>
#include <endian.h>
#include <sys/uio.h>

const char MsgBuffer::kCRLF[] = "\r\n";

void MsgBuffer::appendInt64(int64_t x)
{
    int64_t be64 = htobe64(x);
    append(&be64, sizeof be64);
}

void MsgBuffer::appendInt32(int32_t x)
{
    int32_t be32 = htobe32(x);
    append(&be32, sizeof be32);
}

void MsgBuffer::appendInt16(int16_t x)
{
    int16_t be16 = htobe16(x);
    append(&be16, sizeof be16);
}

int64_t MsgBuffer::peekInt64() const
{
    assert(readableBytes() >= sizeof(int64_t));
    int64_t be64 = 0;
    memcpy(&be64, peek(), sizeof be64);
    return be64toh(be64);
}

int32_t MsgBuffer::peekInt32() const
{
    assert(readableBytes() >= sizeof(int32_t));
    int32_t be32 = 0;
    memcpy(&be32, peek(), sizeof be32);
    return be32toh(be32);
}

int16_t MsgBuffer::peekInt16() const
{
    assert(readableBytes() >= sizeof(int16_t));
    int16_t be16 = 0;
    memcpy(&be16, peek(), sizeof be16);
    return be16toh(be16);
}

void MsgBuffer::prependInt64(int64_t x)
{
    int64_t be64 = htobe64(x);
    prepend(&be64, sizeof be64);
}

void MsgBuffer::prependInt32(int32_t x)
{
    int32_t be32 = htobe32(x);
    prepend(&be32, sizeof be32);
}

void MsgBuffer::prependInt16(int16_t x)
{
    int16_t be16 = htobe16(x);
    prepend(&be16, sizeof be16);
}

ssize_t MsgBuffer::readFd(int fd, int *savedErrno)
{
    char extrabuf[65536];
    struct iovec vec[2];
    const size_t writable = writableBytes();
    vec[0].iov_base = begin() + writerIndex_;
    vec[0].iov_len = writable;
    vec[1].iov_base = extrabuf;
    vec[1].iov_len = sizeof(extrabuf);
    const int iovcnt = (writable < sizeof(extrabuf)) ? 2 : 1;
    const ssize_t n = ::readv(fd, vec, iovcnt);
    if (n < 0)
    {
        *savedErrno = errno;
    }
    else if (static_cast<size_t>(n) <= writable)
    {
        writerIndex_ += n;
    }
    else
    {
        writerIndex_ = buffer_.size();
        append(extrabuf, n - writable);
    }
    return n;
}

void MsgBuffer::makeSpace(size_t len)
{
    if (writableBytes() >= len)
        return;
    // prependable bytes is readerIndex_
    if (readerIndex_ + writableBytes() >=
        (len + kCheapPrepend))  // move readable bytes
    {
        std::copy(begin() + readerIndex_,
                  begin() + writerIndex_,
                  begin() + kCheapPrepend);
        writerIndex_ = kCheapPrepend + (writerIndex_ - readerIndex_);
        readerIndex_ = kCheapPrepend;
        return;
    }
    // create new buffer and resize current buffer by swap
    size_t newLen;
    if ((buffer_.size() * 2) > (kCheapPrepend + readableBytes() + len))
        newLen = buffer_.size() * 2;
    else
        newLen = kCheapPrepend + readableBytes() + len;
    MsgBuffer newbuffer(newLen);
    newbuffer.append(*this);
    swap(newbuffer);
}

void MsgBuffer::append(const MsgBuffer &buffer)
{
    makeSpace(buffer.writableBytes());
    memcpy(&buffer_[writerIndex_], buffer.peek(), buffer.readableBytes());
    writerIndex_ += buffer.readableBytes();
}
